package cartagows.wsframework.wscoordination;

import java.util.HashMap;

import org.apache.axiom.om.OMElement;

import cartago.ArtifactId;
import cartago.CartagoException;
import cartago.Op;
import cartago.OpFeedbackParam;
import cartago.OpRequestTimeoutException;
import cartago.util.agent.NoPerceptionException;
import cartagows.CartagowsParam;
import cartagows.WSMsgInfo;
import cartagows.util.WSAgent;
import cartagows.util.XMLib;
import cartagows.wsframework.core.FaultBuilder;
import cartagows.wsframework.wsaddressing.WSAddressingMessageElem;
import cartagows.wsframework.wsaddressing.WSAddressingParam;
import cartagows.wsframework.wsatomictransaction.ATCoordinatorAgent;
import cartagows.wsframework.wsatomictransaction.WSAtomicTransactionParam;
import cartagows.wsframework.wsbusinessactivities.WSBusinessActivitiesParam;


public class RegistrationService extends WSAgent{

	private int counter;
	private ArtifactId wsPanel;
	private ArtifactId activitiesRegistry;

	public RegistrationService(String agentName) throws CartagoException{
		super(agentName);
		//Creation of the WSPanel used by the RegistrationService
		wsPanel = makeArtifact(WSCoordinationParam.REGISTRATION_SERVICE_WSPANEL_DEF_NAME, 
				CartagowsParam.WSPANEL_CLASS_PATH,
				new Object[]{WSCoordinationParam.WSDL_URI, WSCoordinationParam.REGISTRATION_SERVICE_NAME});

		activitiesRegistry = lookupArtifact(WSCoordinationParam.ACTIVITIES_REGISTRY_DEF_NAME);
		counter = 0;
	}

	public void run(){

		log("[REGISTRATION_SERVICE:]Starting");

		while(true){

			try {

				OMElement outputMsg = null;
				String coordEPR = "";
				OpFeedbackParam<WSMsgInfo> res = new OpFeedbackParam<WSMsgInfo>();
				//Waiting for requests
				log("[REGISTRATION_SERVICE:]Waiting requests");

				doAction(wsPanel, new Op("getWSMsg", res));

				log("[REGISTRATION_SERVICE:]Request found");

				WSMsgInfo inputMsg = res.get();
				String opName = inputMsg.getOperationName();

				//Retrieval of the body and the header of the message
				OMElement msgBody = XMLib.getInstance().buildElementFromString(inputMsg.getBody());
				OMElement msgheader = XMLib.getInstance().buildElementFromString(inputMsg.getHeader());

				//Check if the operation is WSCoordinationWSDLOperation.REGISTER
				if (opName.equals(WSCoordinationWSDLOperation.REGISTER)) {

					//Check of the received message
					String protocolType = XMLib.getInstance().getElementValue(WSCoordinationMessageElem.REGISTER,
							WSCoordinationMessageElem.PROTOCOL_IDENTIFIER, msgBody);
					String activityId = XMLib.getInstance().getElementValue(WSCoordinationMessageElem.COORDINATION_CONTEXT,
							WSCoordinationMessageElem.IDENTIFIER, msgheader);
					String participantEPR = XMLib.getInstance().getElementValue(WSCoordinationMessageElem.REGISTER,
							WSCoordinationMessageElem.PARTICIPANT_PROTOCOL_SERVICE, WSAddressingMessageElem.ADDRESS, msgBody);

					//Error: bad request message
					if(protocolType==null || participantEPR==null){
						log("[REGISTRATION_SERVICE:]Error: Bad message");
						outputMsg = FaultBuilder.getInstance().buildSOAPFault(
								WSCoordinationErrorParam.FAULT_INVALID_PARAMETER_CODE,
								WSCoordinationErrorParam.FAULT_INVALID_PARAMETER_SUBCODE,
								WSCoordinationErrorParam.FAULT_INVALID_PARAMETER_REASON, "");
					}

					//Error: bad protocol provided
					else if(!(protocolType.equals(WSAtomicTransactionParam.VOLATILE_2PC) ||(protocolType.equals(WSAtomicTransactionParam.DURABLE_2PC)) || 
							(protocolType.equals(WSBusinessActivitiesParam.BA_PARTECIPANT_COMPLETION)) ||(protocolType.equals(WSBusinessActivitiesParam.BA_COORDINATOR_COMPLETION)))){

						log("[REGISTRATION_SERVICE:]"+ WSCoordinationErrorParam.ERR_WRONG_COORD_TYPE);

						outputMsg = FaultBuilder.getInstance().buildSOAPFault(
								WSCoordinationErrorParam.FAULT_INVALID_PROTOCOL_CODE, 
								WSCoordinationErrorParam.FAULT_INVALID_PROTOCOL_SUBCODE, 
								WSCoordinationErrorParam.FAULT_INVALID_PROTOCOL_REASON, "");
					}

					//Request message ok
					else{
						OpFeedbackParam<Boolean> checkRes = new OpFeedbackParam<Boolean>();
						//Check if the activity identified by activityId is stored into the activitiesRegistry 
						doAction(activitiesRegistry, new Op("checkActivityPresence", activityId, checkRes));

						//Error: Activity not found (the activityId provided is not valid)
						if(checkRes.get()==false){

							log("[REGISTRATION_SERVICE:]"+ WSCoordinationErrorParam.ERR_BAD_CONTEXT);

							outputMsg = FaultBuilder.getInstance().buildSOAPFault(
									WSCoordinationErrorParam.FAULT_INVALID_PARAMETER_CODE,
									WSCoordinationErrorParam.FAULT_INVALID_PARAMETER_SUBCODE,
									WSCoordinationErrorParam.FAULT_INVALID_PARAMETER_REASON, "");
						}

						//Activity found successfully
						else{

							OpFeedbackParam<HashMap<String,String>> participantsFeedback = new OpFeedbackParam<HashMap<String,String>>();
							doAction(activitiesRegistry, new Op("getActivityParticipantList", activityId, participantsFeedback));
							HashMap<String,String> participantList = participantsFeedback.get();

							//First participant requesting to be registered for the activity, will be spawned the coordinator
							if(participantList.size()==0){
								counter++;
								if(protocolType.equals(WSAtomicTransactionParam.VOLATILE_2PC) || 
										protocolType.equals(WSAtomicTransactionParam.DURABLE_2PC)){

									coordEPR = WSAtomicTransactionParam.DEFAULT_COORD_EPR+counter;
									ATCoordinatorAgent coord = new ATCoordinatorAgent(WSAtomicTransactionParam.COORDINATOR_AGENT_DEF_NAME+counter, activityId);
									coord.start();
									log("[REGISTRATION_SERVICE:]Spawned coordinator "+coordEPR + "for acticity" + activityId);

								}
								else{
									coordEPR = WSBusinessActivitiesParam.BA_DEFAULT_COORD_EPR+counter;
								}
							}
							try{
								//Registration of the participant
								doAction(activitiesRegistry, new Op("registerParticipantForActivity",activityId ,participantEPR, protocolType));
								outputMsg = createRegisterResponseMsg(WSAtomicTransactionParam.DEFAULT_COORD_EPR);
							}
							catch (NoPerceptionException ex) {
								log("[REGISTRATION_SERVICE:]Error, cannot register participant");
								outputMsg = FaultBuilder.getInstance().buildSOAPFault(
										WSCoordinationErrorParam.FAULT_CANNOT_REGISTER_CODE,
										WSCoordinationErrorParam.FAULT_CANNOT_REGISTER_SUBCODE,
										WSCoordinationErrorParam.FAULT_CANNOT_REGISTER_REASON, "");
							}
						}
					}

					try {
						//Send of the reply to the service requestor
						OpFeedbackParam<String> replyId = new OpFeedbackParam<String>();
						doAction(wsPanel, new Op("sendWSReply", inputMsg, outputMsg.toString(), replyId), CartagowsParam.LONG_TIMEOUT);
						log("[REGISTRATION_SERVICE:]Reply sent successfully - id: " + replyId.get());
					} catch (NoPerceptionException ex) {
						log("[REGISTRATION_SERVICE:]Reply failed.");
					}
				} 
			}
			catch(OpRequestTimeoutException ex){
				log("[REGISTRATION_SERVICE:]No msg");
			}

			catch (Exception ex) {
				log("[REGISTRATION_SERVICE:]"+ ex.getMessage() +" "+ ex.getCause());
				ex.printStackTrace();
			}
		}
	}

	public OMElement createRegisterResponseMsg(String coordEPR){

		OMElement msg = XMLib.getInstance().buildElement(WSCoordinationMessageElem.REGISTER_RESPONSE);
		OMElement coordProtocolService = XMLib.getInstance().buildElement(WSCoordinationMessageElem.COORDINATOR_EPR);
		OMElement address = XMLib.getInstance().buildElement(WSAddressingMessageElem.ADDRESS, coordEPR, 
				WSAddressingParam.NAMESPACE, WSAddressingParam.NAMESPACE_PREFIX);
		XMLib.getInstance().addChildNode(coordProtocolService, address);
		XMLib.getInstance().addChildNode(msg, coordProtocolService);
		return msg;
	}
}
